package com.yh.csx.crm.core.common;

import com.alibaba.dubbo.config.annotation.Reference;
import com.yh.b2b.common.response.CommonResultResponse;
import com.yh.b2b.usercenter.common.enums.UserCenterErrorCode;
import com.yh.b2b.usercenter.facade.stub.UserTokenFacade;
import com.yh.b2b.usercenter.facade.stub.util.UserUtils;
import com.yh.b2b.usercenter.facade.stub.vo.UserVO;
import com.yh.csx.crm.core.util.WebUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
import java.util.function.Consumer;

@Slf4j
public class AuthFilter implements Filter {
    private static final String[] AUTH_HEADERS = {"login-token", "x-token"};


    @Reference
    private UserTokenFacade userTokenFacade;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        String curOrigin = request.getHeader("Origin");
        if (curOrigin != null) {
            response.setHeader("Access-Control-Allow-Origin", curOrigin);
        }
        else
        {
            response.setHeader("Access-Control-Allow-Origin", "*");//测试时候用
        }
        String authHeaders = StringUtils.join(AUTH_HEADERS, ", ");
        response.setHeader("Access-Control-Allow-Headers", "origin, content-type, accept, authorization, " + authHeaders);
        response.setHeader("Access-Control-Expose-Headers", authHeaders);
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
        if (HttpMethod.OPTIONS.name().equals(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_NO_CONTENT);
            return;
        }
        WebUtils.WebContext ctx = new WebUtils.WebContext(request, response);
        ctx.setAuth(new Auth(getToken(request), info -> initUser(info, request, response)));
        WebUtils.bindContext(ctx);
        try {
            chain.doFilter(request, response);
        } finally {
            WebUtils.clearContext();
        }
    }

    private String getToken(HttpServletRequest request) {
        for (String header : AUTH_HEADERS) {
            String token = request.getHeader(header);
            if (StringUtils.isNotBlank(token)) return token;
        }
        return null;
    }

    private void initUser(Auth info, HttpServletRequest request, HttpServletResponse response) {
        UserVO userVO = null;
        String newToken = null;
        try {
            CommonResultResponse resp = userTokenFacade.checkToken(info.token);
            if (resp.getCode() == UserCenterErrorCode.SUCCESS.getCode()) {
                Map map = (Map) resp.getResult();
                String token = map.get("token").toString();
                if (StringUtils.isNotBlank(token)) {
                    newToken = token;
                    userVO = (UserVO) map.get("user");
                }
            }
        } catch (Exception e) {
            log.warn("check token fail: " + e.getMessage(), e);
        }
        info.token = newToken;
        info.user = userVO;
        if (userVO != null) {
            UserUtils.set(userVO);
            UserUtils.setToken(newToken);
            if (!"/logout".equals(request.getRequestURI())) {
                for (String header : AUTH_HEADERS) {
                    response.setHeader(header, newToken);
                }
            }
        }
    }

    public class Auth {
        private Boolean login;
        private String token;
        private UserVO user;
        private Consumer<Auth> consumer;

        public Auth(String token, Consumer<Auth> consumer) {
            this.token = token;
            this.consumer = consumer;
        }

        public boolean isLogin() {
            if (login == null) {
                if (token != null && token.length() > 0) {
                    consumer.accept(this);
                    login = user != null;
                } else {
                    login = initTestInfo(this);
                }
            }
            return login;
        }

        public String getToken() {
            return isLogin() ? token : null;
        }

        public UserVO getUser() {
            return isLogin() ? user : null;
        }
    }

    private boolean initTestInfo(Auth auth) {
        if (testEnable) {
            auth.token = testToken;
            auth.user = new UserVO();
            auth.user.setId(testUserId);
            auth.user.setName(testName);
            auth.user.setTelephone(testMobile);
        }
        return testEnable;
    }

    @Value("${test.enable:false}")
    private boolean testEnable;
    @Value("${test.id:10000000}")
    private Long testUserId;
    @Value("${test.mobile:13999999999}")
    private String testMobile;
    @Value("${test.name:测试用户}")
    private String testName;
    @Value("${test.token:112233445566778899aabbccddeeff}")
    private String testToken;
}
